import axios, { AxiosRequestConfig, AxiosResponse } from "axios";
import qs from "qs";
import { Helper } from "./Helper";
import { PromiseInOne } from "./PromisesInOne";

export interface RequestConfig<Body = any> extends AxiosRequestConfig<Body> {
  url: string;
}
export class Http {
  defaultConfig: AxiosRequestConfig<any> = {
    paramsSerializer: {
      serialize: params => qs.stringify(params, { indices: false })
    }
  }
  pio = new PromiseInOne()
  request<Reply = any, Body = any>(method: 'post' | 'put' | 'get' | 'options' | 'head' | 'delete', args: RequestConfig<Body>) {
    const params = Helper.getClearParams(args.params);
    switch (method) {
      case 'post': case 'put':
        return axios[method]<Reply, AxiosResponse<Reply>, Body>(args.url, args.data, { ...args, ...this.defaultConfig, params })
      case 'get': case 'options': case 'head': case 'delete':
        return axios[method]<Reply, AxiosResponse<Reply>, Body>(args.url, { ...args, ...this.defaultConfig, params })
      default:
        throw new Error("mehod not supported: " + method)
    }
  }
  requestInOne<Reply = any, Body = any>(method: 'post' | 'put' | 'get' | 'options' | 'head' | 'delete', args: RequestConfig<Body>) {
    return (args.cancelToken || args.signal) ?
      this.request(method, args) :
      new Promise<AxiosResponse<Reply, Body>>((resolve, reject) => {
        const [pid, existed] = this.pio.intoPool(method + '_', args, resolve, reject)
        if (existed) return
        this.pio.handlePromise(pid, this.request(method, args))
      })
  }
  GET<Reply = any, Body = any>(args: RequestConfig<Body>) { return this.requestInOne<Reply, Body>('get', args) }
  OPTIONS<Reply = any, Body = any>(args: RequestConfig<Body>) { return this.requestInOne<Reply, Body>('options', args) }
  HEAD<Reply = any, Body = any>(args: RequestConfig<Body>) { return this.requestInOne<Reply, Body>('head', args) }
  DELETE<Reply = any, Body = any>(args: RequestConfig<Body>) { return this.requestInOne<Reply, Body>('delete', args) }
  POST<Reply = any, Body = any>(args: RequestConfig<Body>) { return this.requestInOne<Reply, Body>('post', args) }
  PUT<Reply = any, Body = any>(args: RequestConfig<Body>) { return this.requestInOne<Reply, Body>('put', args) }
  TRACE<Reply = any, Body = any>(args: RequestConfig<Body>): Promise<AxiosResponse<Reply, Body>> { throw new Error('[HTTP]TRACE not sopported') }
  CONNECT<Reply = any, Body = any>(args: RequestConfig<Body>): Promise<AxiosResponse<Reply, Body>> { throw new Error('[HTTP]CONNECT not sopported') }
}

export const http = new Http